/*
 * IStrutureBTreeImpl.cpp
 *
 *      Author: carlos
 */

#include "IStrutureBTreeImpl.h"
#include "../../utils/const.h"
#include "../../physical_layer/IDGenerator.h"
#include "../../utils/StringUtils.h"
#include "../../utils/ConfigurationMannagerPool.h"
#include "../EntityFactory.h"
#include "../bplustree/ElementFactory.h"
IStrutureBTreeImpl::IStrutureBTreeImpl(ESTRUCTURAS estructura) :
		IStructureNew(estructura) {
	ConfigurationMananger* c = ConfigurationMannagerPool::getInstance(
			estructura);
	this->bplusTree = new BPlusTree(c->getBufferSizeTree(), c->getFilename(),
			estructura);

	this->hojaActual = NULL;
}

IStrutureBTreeImpl::~IStrutureBTreeImpl() {
	delete this->bplusTree;
}

GenericIndexable* IStrutureBTreeImpl::findGenericIndexable(string key) {
	GenericKey genericKey(key);
	//Un nodo tiene muchas IEntidad. Buscamos la entidad a ver si esta
	try {
		Element* element = (Element*) this->bplusTree->findExact(key);
		IEntidad* entidad = EntityFactory::createEntity(
				element->getEstructura(), element);
		return (GenericIndexable*) entidad;

	} catch (ElementNotFoundException* e) {
		//todo revisar que no arroje un new
		delete e;
		return NULL;
	}
}

IEntidad* IStrutureBTreeImpl::find(string key) {
	GenericKey genericKey(key);
	//Un nodo tiene muchas IEntidad. Buscamos la entidad a ver si esta
	try {
		Element* element = (Element*) this->bplusTree->findExact(key);
		IEntidad* entidad = EntityFactory::createEntity(
				element->getEstructura(), element);
		return entidad;

	} catch (ElementNotFoundException* e) {
		//todo revisar que no arroje un new
		delete e;
		return NULL;
	} catch (ElementNotFoundException& e) {
		return NULL;
	}
}

IEntidad* IStrutureBTreeImpl::findAproximado(string key) {
	GenericKey genericKey(key);
	this->hojaActual = this->bplusTree->find(genericKey);
	return this->siguiente();

}
/**
 * El buscar en el nodo el siguiente por id. Si no hay nodo, buscar un nuevo nodo.
 */
IEntidad* IStrutureBTreeImpl::siguiente() {
	IElement * element = NULL;
	if (this->hojaActual == NULL) {
		this->hojaActual = this->bplusTree->next();
		element = this->hojaActual->nextElement();
		if (element == NULL) { //no hay mas elementos en esta hoja voy a la siguiente si hay
			if (this->hojaActual->getNextNode() == -1) {
				return NULL;
			}
			this->hojaActual = this->bplusTree->next();
			return this->siguiente();
		}
	}else{
		element=this->hojaActual->nextElement();
	}
	if(element==NULL){
		return NULL;
	}
	return EntityFactory::createEntity(this->getEstructura(), element);
}

IEntidad* IStrutureBTreeImpl::anterior() {
	IElement * element = NULL;
	if (this->hojaActual == NULL) {
		this->hojaActual = this->bplusTree->prev();
		element = this->hojaActual->nextElement();
		if (element == NULL) { //no hay mas elementos en esta hoja voy a la siguiente si hay
			if (this->hojaActual->getPrevNode() == -1) {
				return NULL;
			}

			this->hojaActual = this->bplusTree->prev();
			return this->anterior();
		}
	}else{
		element=this->hojaActual->nextElement();
	}
	if(element==NULL){
		return NULL;
	}

	return EntityFactory::createEntity(this->getEstructura(), element);
}
int IStrutureBTreeImpl::remove(string key) {
	GenericKey genericKey(key);
	this->bplusTree->remove(key);
	return 0;

}

int IStrutureBTreeImpl::insert(IEntidad *entidad) {

	//como yo en el arbol guardo key,string, convierto la key de la entidad y el vector a string

	ConfigurationMananger* c = ConfigurationMannagerPool::getInstance(
			this->getEstructura());
	if (entidad->getID() == 0 && !entidad->isStringId()) {
		entidad->setID(
				IDGenerator::getInstance()->getNextID(c->getSequenceName()));
	} else {
		string mensaje;
		mensaje.append("IStructure BTree ").append(
				ConfigurationMannagerPool::getStructureName(
						this->getEstructura())).append(
				"esta insertando un elemento con identificador: ").append(
				StringUtils::convertIntToString(entidad->getID()));
		//Logger::getInstance()->warn(mensaje);
		this->allowException(string("IStructureBTreeImpl").append(mensaje));
	}
	Element* element = NULL;
	if (!entidad->isStringId()) {
		element = ElementFactory::createElement(this->getEstructura(),
				entidad->getID(), entidad);
	} else {
		element = ElementFactory::createElement(this->getEstructura(),
				entidad->getIdstring(), entidad);
	}

	try {
		this->bplusTree->insert(element);
	} catch (ElementAlreadyExists& e) {
		return 1;
	}

	return 0;
}

void IStrutureBTreeImpl::allowException(string mensaje) {
	//TERRIBLE HACKKK
	//TODO FIXME!!!! Hay una entidad no autoincremental. Hay que crear IStructures con esa particularidad o configurables para soportarlo

}

int IStrutureBTreeImpl::modify(IEntidad *entidad) {
	Element* el = NULL;
	if (!entidad->isStringId()) {
		el = ElementFactory::createElement(this->getEstructura(),
				entidad->getID(), entidad);

	} else {
		el = ElementFactory::createElement(this->getEstructura(),
				entidad->getIdstring(), entidad);
	}
	//todo trycatchear la exception
	this->bplusTree->modify(el);
	return 0;

}

void IStrutureBTreeImpl::toString() {
	this->bplusTree->exportTree();
}

void IStrutureBTreeImpl::dropStructure() {
	this->bplusTree->deleteTree();
}

